﻿using System;
using System.Xml;
using Notoric.Model.Collections;
using Notoric.Model.Internal;
using Notoric.Model.Packaging;
using Notoric.Model.Validation;

namespace Notoric.Model.Elements
{
    /// <summary>
    /// Abstract base class for block durational symbols (tremolo and tuplets).
    /// </summary>
    public abstract class BlockSymbolElement : DurationalSymbolElement
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="BlockSymbolElement"/> instance.
        /// </summary>
        /// <param name="packagePart">
        /// <para>
        /// Type: <see cref="CompositionPart"/>
        /// </para>
        /// <para>
        /// The package part that owns this element.
        /// </para>
        /// </param>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="BeatElement"/>
        /// </para>
        /// <para>
        /// The beat that directly owns this symbol.
        /// </para>
        /// </param>
        /// <param name="voice">
        /// <para>
        /// Type: <see cref="VoiceDescriptorElement"/>
        /// </para>
        /// <para>
        /// The descriptor of the voice that this symbol belongs to.
        /// </para>
        /// </param>
        /// <param name="duration">
        /// <para>
        /// Type: <see cref="System.UInt16"/>
        /// </para>
        /// <para>
        /// The duration of the symbol.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="packagePart"/>, <paramref name="owner"/> or <paramref name="voice"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ValidationException">
        /// The value of <paramref name="voice"/> or <paramref name="duration"/> is invalid.
        /// Please, refer to the documentation of the respective properties for details.
        /// </exception>
        protected BlockSymbolElement(CompositionPart packagePart, BeatElement owner, VoiceDescriptorElement voice, ushort duration)
            : base(packagePart, owner, voice, duration)
        {
            symbolsCollection = new ElementCollection<DurationalSymbolElement>(this, symbolsElement);
            symbols = new ReadOnlyCollection<DurationalSymbolElement>(symbolsCollection);
        }

        /// <summary>
        /// Creates a new <see cref="BlockSymbolElement"/> instance.
        /// </summary>
        /// <param name="packagePart">
        /// <para>
        /// Type: <see cref="CompositionPart"/>
        /// </para>
        /// <para>
        /// The package part that directly owns this element.
        /// </para>
        /// </param>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="BeatElement"/>
        /// </para>
        /// <para>
        /// The beat that directly owns this symbol.
        /// </para>
        /// </param>
        /// <param name="element">
        /// <para>
        /// Type: <see cref="XmlElement"/>
        /// </para>
        /// <para>
        /// The XML element that holds the data of this object.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Any of the arguments is <see langword="null"/>.
        /// </exception>
        protected BlockSymbolElement(CompositionPart packagePart, BeatElement owner, XmlElement element)
            : base(packagePart, owner, element)
        {
            symbolsElement = element[PackageVocabulary.DurationalSymbolElementName + "s", PackageVocabulary.PackageNamespaceUri];

            symbolsCollection = new ElementCollection<DurationalSymbolElement>(this, symbolsElement);
            symbols = new ReadOnlyCollection<DurationalSymbolElement>(symbolsCollection);
        }

        #endregion

        #region DurationalSymbolElement Members

        /// <inheritdoc/>
        protected override XmlElement CreateObjectElementCore(XmlDocument document)
        {
            var element = CreateSymbolElementCore(document);

            symbolsElement = document.CreateElement(
                PackageVocabulary.PackageNamespacePrefix,
                PackageVocabulary.DurationalSymbolElementName + "s",
                PackageVocabulary.PackageNamespaceUri
                );

            element.AppendChild(symbolsElement);

            return element;
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// When overriden in a derived class, this method creates an XML element that will
        /// holds the data of this symbol. The common part (of block and durational symbol)
        /// will be added by the respective base classes.
        /// </summary>
        /// <remarks>
        /// This method is called from <see cref="CreateObjectElementCore"/>.
        /// </remarks>
        /// <param name="document">
        /// <para>
        /// Type: <see cref="XmlDocument"/>
        /// </para>
        /// <para>
        /// The XML document used for creating elements and attributes.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="XmlElement"/>
        /// </para>
        /// <para>
        /// The created symbol element.
        /// </para>
        /// </returns>
        protected abstract XmlElement CreateSymbolElementCore(XmlDocument document);

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the collection of symbols that form this block symbol.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="ReadOnlyCollection{T}"/> of <see cref="DurationalSymbolElement"/>
        /// </para>
        /// The collection of symbols that form this block symbol. 
        /// The collection is ordered from the leftmost (earliest) to the rightmost (latest) symbol.
        /// Each symbol begins immediately after the previous symbol.
        /// </value>
        public ReadOnlyCollection<DurationalSymbolElement> Symbols
        {
            get { return symbols; }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the value of the <see cref="Symbols"/> property.
        /// </summary>
        private ReadOnlyCollection<DurationalSymbolElement> symbols;
        /// <summary>
        /// The collection of symbols that form this block symbol.
        /// </summary>
        private ElementCollection<DurationalSymbolElement> symbolsCollection;
        /// <summary>
        /// The XML element that holds the <see cref="symbolsCollection"/>.
        /// </summary>
        private XmlElement symbolsElement;

        #endregion
    }
}
